#define bit0	1
#define bit1	1<<1
#define bit2	1<<2
#define bit3	1<<3
#define bit4	1<<4
#define bit5	1<<5
#define bit6	1<<6
#define bit7	1<<7
#define bit8	1<<8
#define bit9	1<<9
#define bit10	1<<10
#define bit11	1<<11
#define bit12	1<<12
#define bit13	1<<13
#define bit14	1<<14
#define bit15	1<<15
#define bit16	1<<16
#define bit17	1<<17
#define bit18	1<<18
#define bit19	1<<19
#define bit20	1<<20
#define bit21	1<<21
#define bit22	1<<22
#define bit23	1<<23
#define bit24	1<<24
#define bit25	1<<25
#define bit26	1<<26
#define bit27	1<<27
#define bit28	1<<28
#define bit29	1<<29
#define bit30	1<<30
#define bit31	1<<31
#ifdef __USE_CMSIS
#include "LPC17xx.h"
#endif

#include <cr_section_macros.h>
#include <NXP/crp.h>

// Variable to store CRP value in. Will be placed automatically
// by the linker when "Enable Code Read Protect" selected.
// See crp.h header for more information
__CRP const unsigned int CRP_WORD = CRP_NO_CRP ;

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void I2C1_IRQHandler(void);

// MACROS I2C1
#define I2C1_CLR_INT_FLAG() 		LPC_I2C1->I2CONCLR = 0x00000008   // Clear flag
#define I2C1_ENABLE()          		LPC_I2C1->I2CONSET = 0x00000040   // Enable I2C module
#define I2C1_DISABLE()         		LPC_I2C1->I2CONCLR = 0x00000040   // Disable I2C module (use as regular I/O pins)
#define I2C1_START_TX()				LPC_I2C1->I2CONSET = 0x00000020   // Sends the start command
#define I2C1_KEEP_REPEAT_START()  	LPC_I2C1->I2CONCLR = 0x00000020   // Keep a repeated start from happening
#define I2C1_STOP_TX()         		LPC_I2C1->I2CONSET = 0x00000010   // Sends a stop command
#define I2C1_ACK()             		LPC_I2C1->I2CONSET = 0x00000004   // Sends an acknowledge
#define I2C1_NACK()            		LPC_I2C1->I2CONCLR = 0x00000004   // Sends a Not acknowledged


// MACRO LCD03 - passer le buffer et le pointeur initialises pour travailler avec le LCD
#define LCD03_CURSOR_HOME(buf, ptr)			ptr=0; sprintf(buf,"%c%c", 1, 0); I2C1_START_TX();
#define LCD03_SET_CURSOR(buf, ptr, pos)		ptr=0; sprintf(buf,"%c%c%c", 2, pos, 0); I2C1_START_TX();  // pos = 80 max
#define LCD03_SET_CURSORXY(buf, ptr, l, c)	ptr=0; sprintf(buf,"%c%c%c%c", 3, l, c, 0); I2C1_START_TX();
#define LCD03_HIDE_CURSOR(buf, ptr)			ptr=0; sprintf(buf,"%c%c", 4, 0); I2C1_START_TX();
#define LCD03_UNDERLINE_CURSOR(buf, ptr)	ptr=0; sprintf(buf,"%c%c", 5, 0); I2C1_START_TX();
#define LCD03_BLINK_CURSOR(buf, ptr)		ptr=0; sprintf(buf,"%c%c", 6, 0); I2C1_START_TX();
#define LCD03_BACKSPACE(buf, ptr)			ptr=0; sprintf(buf,"%c%c", 8, 0); I2C1_START_TX();
#define LCD03_HTAB(buf, ptr)				ptr=0; sprintf(buf,"%c%c", 9, 0); I2C1_START_TX();
#define LCD03_LINE_FEED(buf, ptr)			ptr=0; sprintf(buf,"%c%c", 10, 0); I2C1_START_TX();
#define LCD03_VTAB(buf, ptr)				ptr=0; sprintf(buf,"%c%c", 11, 0); I2C1_START_TX();
#define LCD03_CLEAR_HOME(buf, ptr)			ptr=0; sprintf(buf,"%c%c", 12, 0); I2C1_START_TX();
#define LCD03_CR(buf, ptr)					ptr=0; sprintf(buf,"%c%c", 13, 0); I2C1_START_TX();
#define LCD03_CLEAR_COL(buf, ptr)			ptr=0; sprintf(buf,"%c%c", 17, 0); I2C1_START_TX();
#define LCD03_TAB_SET(buf, ptr, tab)		ptr=0; sprintf(buf,"%c%c%c", 18, tab, 0); I2C1_START_TX();
#define LCD03_BACK_ON(buf, ptr)				ptr=0; sprintf(buf,"%c%c", 19, 0); I2C1_START_TX();
#define LCD03_BACK_OFF(buf, ptr)			ptr=0; sprintf(buf,"%c%c", 20, 0); I2C1_START_TX();
#define LCD03_STARTMSG_OFF(buf, ptr)		ptr=0; sprintf(buf,"%c%c", 21, 0); I2C1_START_TX();
#define LCD03_STARTMSG_ON(buf, ptr)			ptr=0; sprintf(buf,"%c%c", 22, 0); I2C1_START_TX();
#define LCD03_NEW_ADDRESS(buf, ptr, addr)	ptr=0; sprintf(buf,"%c%c", 25, addr, 0); I2C1_START_TX();

#define LCD03_MSG(buf, ptr, msg)			ptr=0; sprintf(buf, "%s", msg); I2C1_START_TX(); // msg est nul terminated

char *lcdData;
int dataPtr;
int lcd03Address = 0xc6;
int i2cStatus;

// ! SEMIHOSTING
int main(void)
{
	int i;
	lcdData = (char*)malloc(sizeof(char)*64);
    // I2C vers LCD03
	// P0.0 = SDA1, P0.1 = SCL1

    // PIN CONFIG pour I2C1 -> fonction '11' pour bit0 a bit3
	LPC_PINCON->PINSEL0 |= (bit0|bit1|bit2|bit3);

	// MODE -> ni resistance pull-up, ni pull-down  -> fonction '01'
	LPC_PINCON->PINMODE0 |= (bit1|bit3);
	LPC_PINCON->PINMODE0 &= ~(bit0|bit2);

	// MODE OD -> pins en Open Drain -> bit0 & 1 a 1
	LPC_PINCON->PINMODE_OD0 |= (bit0|bit1);

	// on prend PCLKSEL1 = CCLK -> fonction '10'
	// PCLKSEL1 (pour I2C1) =  CCLK -> bit6=0, bit7=1
	LPC_SC->PCLKSEL1 |= bit7;
	LPC_SC->PCLKSEL1 &= ~bit6;
	
    // CCLK = 100 MH et on veut I2C clock = 100 kHz
	// I2Cfreq = PCLK/(I2C1SCLH+I2SCLL) -> 100.000.000/1.000=100.000 => CLH+CLL=1000
	LPC_I2C1->I2SCLH = 500;
	LPC_I2C1->I2SCLL = 500;  // 500, 500 -> Duty cycle 50% 50%
  printf("-> 2C1 configure\n");
	
	// Master Transmit Mode
	LPC_I2C1->I2CONSET |= (bit6); // I2EN
	LPC_I2C1->I2CONCLR |= (bit5|bit4|bit3|bit2); // STA, STO, SI & AA reset
  printf("-> Master Transmit\n");

    //- Enable Interrupt
  NVIC_EnableIRQ(I2C1_IRQn);
  printf("-> interruption I2C1 activee\n");

  LPC_I2C1->I2CONSET = 0x0000004C; //- I2EN<6>| SI<3> |AA<2>
  printf("0x4C\n");


  LCD03_MSG(lcdData, dataPtr, "LCD03 DISPLAY");
  for(i=0; i < 5000000; i++);

  LCD03_BACK_ON(lcdData, dataPtr);
  for(i=0; i < 50000; i++);

  LCD03_MSG(lcdData, dataPtr, "LCD03 DISPLAY");
  for(i=0; i < 5000000; i++);

  LCD03_BACK_ON(lcdData, dataPtr);

  dataPtr = 0;
  strcpy(lcdData, "xDrive");
  I2C1_START_TX();  //LPC_I2C1->I2CONSET = 0x00000020;
  printf("START TX -> 0x20\n");

  for(i=0; i < 1000000; i++);

  dataPtr = 0;
  strcpy(lcdData, " Clear in 2 secs");
  I2C1_START_TX();

  for(i=0; i < 4000000; i++);

  LCD03_CLEAR_HOME(lcdData, dataPtr);

  for(i=0; i < 3000000; i++);

  LCD03_MSG(lcdData, dataPtr, "Back On");

  for(i=0; i < 3000000; i++);

  LCD03_BACK_ON(lcdData, dataPtr);

  for(i=0; i < 9000000; i++);


  LCD03_MSG(lcdData, dataPtr, "Back Off");

  for(i=0; i < 1000000; i++);

  LCD03_BACK_OFF(lcdData, dataPtr);

	printf("Attente...\n");


	// Enter an infinite loop, just incrementing a counter
    i = 0;
	while(1)
	{
		i++ ;
	}
	return 0 ;
}

// Remplace le handler par defaut
// les printf ralentissent fortement l'affichage -> style "minitel"...
void I2C1_IRQHandler(void)
{
    switch( LPC_I2C1->I2STAT  ) //-I2C interrupt cause/mode
    {
    	// MASTER TRANSMIT
    	//-START/REPEATED CONDITION TRANSMITTED
        case 0x08:
        case 0x10:
//printf("0x08 or 0x10 START/REPEATED START\n");
        /*
        //-Do a repeated start if requested (begins in case 18h|20h|40h|48h)
        //   if (!(I2cStatus & I2C_RESTART))
                i2c_no_start();
        //-transmit address
            I2C_DATA = *I2cDataPtr;
          */
        LPC_I2C1->I2CONCLR = 0x00000020;   ///< Keep a repeated start from happening
        LPC_I2C1->I2DAT = lcd03Address; // adresse esclave
        break;
//-SLAVE ADDRESS + WRITE TRANSMITTED - ACK RECEIVED
        case 0x18:
//printf("0x18 SLAVE ADDRESS + WRITE\n");
        //-Device has acknowledged that the master is talking to it so... send first 'data' byte
        // !!! Seulement pour LCD03 (le premier byte est toujours ignore par LCD03 !)
        // Sinon transmettre le premier byte
            LPC_I2C1->I2DAT = '?';
            //LPC_I2C1->I2DAT = *(data+dataPtr);
            //dataPtr++;
        break;
//-SLAVE ADDRESS + WRITE TRANSMITTED - NO ACK RECEIVED
        case 0x20:
//printf("0x20 SLAVE ADDRESS + WRITE NO ACK RECEIVED\n");
         //-Device is too busy to respond or is not present.
         //    I2cStatus = I2C_ERROR;     //-Transaction status
         I2C1_STOP_TX();//  LPC_I2C1->I2CONSET = 0x00000010;
        break;
//-DATA BYTE - ACK RECEIVED
        case 0x28:
//printf("0x28 SLAVE ADDRESS + WRITE NO ACK RECEIVED\n");
/*
            //-first data byte was received so send next... if more
            if (I2cNumByte > 0)
            {
                ++I2cDataPtr;
                I2C_DATA = *I2cDataPtr;    //-Send 'data byte' 2 through...
                --I2cNumByte;
            }
            else if (I2cStatus & I2C_RESTART)
            {
                I2cDataPtr -= I2cReStartIdx;  //-point to beginning (addr)
                *I2cDataPtr |= READ_I2C_DATA; //-Force read
                I2cNumByte = I2cNumRxByte;
                I2cStatus &= (~I2C_RESTART);  //-Clear flag
                i2c_start_tx();
            }//-send stop command since there are no more bytes to send
            else
            {
                i2c_stop_tx();
                I2cStatus = I2C_OK;
            }
*/
         if ( *(lcdData+dataPtr) != '\0' )
         {
          LPC_I2C1->I2DAT = *(lcdData+dataPtr);
          dataPtr++;
         }
         else
          I2C1_STOP_TX();  // LPC_I2C1->I2CONSET = 0x00000010;   // Sends a stop command
        break;
//-DATA BYTE TRANSMITTED - NO ACK RECEIVED
        case 0x30:
//printf("0x30 SLAVE ADDRESS + WRITE NO ACK RECEIVED\n");
/*
            i2c_stop_tx();
            I2cStatus = I2C_ERROR;
*/
        break;
//-Arbitration lost
        case 0x38:
            I2C1_STOP_TX();             //-Release bus
            // TODO Create auto-retry for ${x} number of times.
        break;
//-UNKNOWN STATE
        default:
            I2C1_STOP_TX();
            //I2cStatus = I2C_ERROR;
    }/*End Switch*/
    //////////////
	LPC_I2C1->I2CONCLR = 0x00000008; // i2c_clr_intrpt_flag();  //-clear the I2C interrupt flag.
}

